home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
fish
/
676-700
/
681
/
term
/
source.lha
/
FileBuffer.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-09
|
18KB
|
826 lines
/*
** $Id: FileBuffer.c,v 1.1 92/04/03 20:41:56 olsen Sta Locker: olsen $
** $Revision: 1.1 $
** $Date: 92/04/03 20:41:56 $
**
** Double-buffered file I/O routines
**
** Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
** All Rights Reserved
*/
#include "termGlobal.h"
/* Size of a file buffer. */
#define BUFFER_SIZE 32768
/* The two message signals. */
#define BUFFER_COMMAND SIGBREAKF_CTRL_E
#define BUFFER_SYNC SIGBREAKF_CTRL_F
/* Argument types. */
enum { ARG_NAME,ARG_MODE };
enum { ARG_OFFSET,ARG_ORIGIN };
/* Seek offsets. */
enum { SEEK_SET,SEEK_CURR,SEEK_END };
/* Command codes. */
enum { BUF_CLOSE,BUF_SEEK,BUF_FILL,BUF_FLUSH };
/* FileBufferServer():
*
* Background process to handle the buffering
* of a filehandle, automatically gets invoked
* when a file is opened.
*/
STATIC VOID __saveds
FileBufferServer()
{
struct MsgPort *Port;
struct Buffer *Buffer;
BYTE Terminated = FALSE,Done,WasFull;
UBYTE *String;
APTR Data;
LONG Length;
BPTR SomeLock;
/* Wait for startup message (-> Buffer). */
Port = &((struct Process *)SysBase -> ThisTask) -> pr_MsgPort;
WaitPort(Port);
Buffer = (struct Buffer *)GetMsg(Port);
/* Open the file and obtain a filehandle. */
String = (UBYTE *)Buffer -> ActionData[ARG_MODE];
Buffer -> WriteAccess = TRUE;
/* Put the message into the list. */
ObtainSemaphore(&DoubleBufferSemaphore);
AddTail(&DoubleBufferList,(struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
/* Check for the open type. */
switch(String[0])
{
case 'r': if(String[1] == '+')
Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_READWRITE);
else
{
Buffer -> WriteAccess = FALSE;
Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_OLDFILE);
}
break;
case 'w': if(String[1] == '+')
{
if(SomeLock = Lock((UBYTE *)Buffer -> ActionData[ARG_NAME],ACCESS_WRITE))
{
UnLock(SomeLock);
DeleteFile((UBYTE *)Buffer -> ActionData[ARG_NAME]);
}
Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_READWRITE);
}
else
Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_NEWFILE);
break;
case 'a': if(SomeLock = Lock((UBYTE *)Buffer -> ActionData[ARG_NAME],ACCESS_WRITE))
{
UnLock(SomeLock);
if(Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_READWRITE))
{
if(Seek(Buffer -> FileHandle,0,OFFSET_END) == -1)
{
Close(Buffer -> FileHandle);
Buffer -> FileHandle = NULL;
}
}
}
else
Buffer -> FileHandle = Open((UBYTE *)Buffer -> ActionData[ARG_NAME],MODE_NEWFILE);
break;
default: break;
}
/* Clear signal bit. */
SetSignal(0,BUFFER_COMMAND);
/* Did the file open? */
if(Buffer -> FileHandle)
{
Buffer -> Data = Buffer -> DataBuffer[0];
Buffer -> DataCount = 1;
Buffer -> Fresh = TRUE;
/* If not in write mode fill the buffers. */
if(!Buffer -> WriteAccess)
{
/* Fill the first one synchronously. */
Buffer -> ReadBufFull = Read(Buffer -> FileHandle,Buffer -> Data,Buffer -> BufLength);
Buffer -> Read = TRUE;
Buffer -> RealPosition = Buffer -> ReadBufFull;
/* Restart caller. */
Signal(Buffer -> Caller,BUFFER_SYNC);
/* Fill the second buffe asynchronously. */
Buffer -> DataLength[1] = Buffer -> Cached = Read(Buffer -> FileHandle,Buffer -> DataBuffer[1],Buffer -> BufLength);
Buffer -> RealPosition += Buffer -> Cached;
}
else
{
UBYTE TempBuffer[256],*Stop;
strcpy(TempBuffer,(UBYTE *)Buffer -> ActionData[ARG_NAME]);
Stop = PathPart(TempBuffer);
*Stop = 0;
if(Buffer -> DirLock = Lock(TempBuffer,ACCESS_READ))
{
if(!Info(Buffer -> DirLock,&Buffer -> InfoData))
{
UnLock(Buffer -> DirLock);
Buffer -> DirLock = NULL;
}
}
Signal(Buffer -> Caller,BUFFER_SYNC);
}
}
else
Terminated = TRUE;
/* Go into loop waiting for commands. */
while(!Terminated)
{
Wait(BUFFER_COMMAND);
Done = FALSE;
Buffer -> Result = 0;
/* Take care of each action. */
switch(Buffer -> Action)
{
/* Close the file, flush any dirty
* buffers and exit.
*/
case BUF_CLOSE: Buffer -> Result = TRUE;
if(Buffer -> BufPosition && Buffer -> Written)
{
if(Write(Buffer -> FileHandle,Buffer -> Data,Buffer -> BufPosition) != Buffer -> BufPosition)
Buffer -> Result = FALSE;
}
if(!Close(Buffer -> FileHandle))
Buffer -> Result = FALSE;
if(Buffer -> DirLock)
UnLock(Buffer -> DirLock);
Terminated = TRUE;
break;
/* Seek to a specific file position. */
case BUF_SEEK: Buffer -> Result = 0;
/* Do nothing if buffer is still
* untouched and we are required
* to seek back to the beginning
* of the file.
*/
if(Buffer -> Fresh && !Buffer -> ActionData[ARG_OFFSET] && Buffer -> ActionData[ARG_ORIGIN] == SEEK_SET)
{
Signal(Buffer -> Caller,BUFFER_SYNC);
Done = TRUE;
}
else
{
Buffer -> WriteBufFull = Buffer -> BufLength;
Buffer -> Read = FALSE;
if(Buffer -> BufPosition && Buffer -> Written)
{
if(Write(Buffer -> FileHandle,Buffer -> Data,Buffer -> BufPosition) != Buffer -> BufPosition)
Buffer -> Result = -1;
}
if(!Buffer -> Result)
{
Buffer -> Result = Buffer -> RealPosition - (Buffer -> ReadBufFull + Buffer -> Cached);
switch(Buffer -> ActionData[ARG_ORIGIN])
{
case SEEK_SET: if(Seek(Buffer -> FileHandle,Buffer -> ActionData[ARG_OFFSET],OFFSET_BEGINNING) != -1)
Buffer -> RealPosition = Buffer -> ActionData[ARG_OFFSET];
else
Buffer -> Result = -1;
break;
case SEEK_CURR: if(!Buffer -> WriteAccess && Buffer -> ActionData[ARG_OFFSET] >= 0 && Buffer -> ReadBufFull - Buffer -> ActionData[ARG_OFFSET] >= 0)
{
Buffer -> ReadBufFull -= Buffer -> ActionData[ARG_OFFSET];
Buffer -> Data += Buffer -> ActionData[ARG_OFFSET];
Signal(Buffer -> Caller,BUFFER_SYNC);
Done = TRUE;
break;
}
if(Seek(Buffer -> FileHandle,-(Buffer -> ReadBufFull + Buffer -> Cached) + Buffer -> ActionData[ARG_OFFSET],OFFSET_CURRENT) != -1)
Buffer -> RealPosition += -(Buffer -> ReadBufFull + Buffer -> Cached) + Buffer -> ActionData[ARG_OFFSET];
else
Buffer -> Result = -1;
break;
case SEEK_END: if(Seek(Buffer -> FileHandle,Buffer -> ActionData[ARG_OFFSET],OFFSET_END) != -1)
Buffer -> RealPosition = Seek(Buffer -> FileHandle,0,OFFSET_CURRENT);
else
Buffer -> Result = -1;
break;
default: Buffer -> Result = -1;
}
Buffer -> ReadBufFull = 0;
if(Buffer -> Result != -1)
{
Buffer -> Data = Buffer -> DataBuffer[0];
Buffer -> DataCount = 1;
if(!Buffer -> WriteAccess)
{
Buffer -> ReadBufFull = Read(Buffer -> FileHandle,Buffer -> Data,Buffer -> BufLength);
Buffer -> WriteBufFull = 0;
Buffer -> Read = TRUE;
Buffer -> RealPosition += Buffer -> ReadBufFull;
if(Buffer -> ReadBufFull)
{
Buffer -> Cached = Buffer -> DataLength[1] = Read(Buffer -> FileHandle,Buffer -> DataBuffer[1],Buffer -> BufLength);
Buffer -> RealPosition += Buffer -> Cached;
}
}
}
else
Buffer -> LastActionFailed = TRUE;
}
else
Buffer -> ReadBufFull = 0;
Buffer -> BufPosition = 0;
Buffer -> Written = FALSE;
}
break;
/* Fill the buffer with fresh data. */
case BUF_FILL: Buffer -> Data = Buffer -> DataBuffer[Buffer -> DataCount];
Buffer -> ReadBufFull = Buffer -> DataLength[Buffer -> DataCount];
Buffer -> WriteBufFull = 0;
Buffer -> BufPosition = 0;
Buffer -> Read = TRUE;
Buffer -> Written = FALSE;
Buffer -> Fresh = FALSE;
if(Buffer -> ReadBufFull)
WasFull = TRUE;
else
WasFull = FALSE;
/* The buffer contents have been
* swapped, now wake the caller
* up and fill the next buffer
* asynchronously.
*/
Signal(Buffer -> Caller,BUFFER_SYNC);
Done = TRUE;
if(WasFull)
{
Buffer -> DataCount = (Buffer -> DataCount + 1) % BUFFER_NUMBER;
Buffer -> Cached = Buffer -> DataLength[Buffer -> DataCount] = Read(Buffer -> FileHandle,Buffer -> DataBuffer[Buffer -> DataCount],Buffer -> BufLength);
Buffer -> RealPosition += Buffer -> Cached;
if(!Buffer -> DataLength[Buffer -> DataCount])
{
if(IoErr())
Buffer -> LastActionFailed = TRUE;
}
}
break;
/* Flush the contents of the buffer to disk. */
case BUF_FLUSH: if(Buffer -> BufPosition && Buffer -> Written)
{
Data = Buffer -> Data;
Length = Buffer -> BufPosition;
Buffer -> Data = Buffer -> DataBuffer[Buffer -> DataCount];
Buffer -> DataCount = (Buffer -> DataCount + 1) % BUFFER_NUMBER;
Buffer -> ReadBufFull = 0;
Buffer -> WriteBufFull = Buffer -> BufLength;
Buffer -> BufPosition = 0;
Buffer -> Read = FALSE;
Buffer -> Written = FALSE;
Signal(Buffer -> Caller,BUFFER_SYNC);
Done = TRUE;
if(Write(Buffer -> FileHandle,Data,Length) != Length)
Buffer -> LastActionFailed = TRUE;
else
{
Buffer -> RealPosition += Length;
if(Buffer -> DirLock)
{
if(!Info(Buffer -> DirLock,&Buffer -> InfoData))
{
UnLock(Buffer -> DirLock);
Buffer -> DirLock = NULL;
}
}
}
}
else
{
Buffer -> ReadBufFull = 0;
Buffer -> WriteBufFull = Buffer -> BufLength;
Buffer -> BufPosition = 0;
Buffer -> Read = FALSE;
Buffer -> Written = FALSE;
}
Buffer -> Fresh = FALSE;
break;
}
/* Ring back if necessary. */
if(!Done && !Terminated)
Signal(Buffer -> Caller,BUFFER_SYNC);
}
/* Remove the message from the list. */
ObtainSemaphore(&DoubleBufferSemaphore);
Remove((struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
/* Lock & quit. */
Forbid();
Signal(Buffer -> Caller,BUFFER_SYNC);
}
/* BufferFill(struct Buffer *Buffer):
*
* Fills a given buffer with fresh data.
*/
STATIC BYTE __regargs
BufferFill(struct Buffer *Buffer)
{
if(Buffer -> LastActionFailed)
return(FALSE);
else
{
if(!Buffer -> ReadBufFull)
{
Buffer -> Action = BUF_FILL;
Signal(Buffer -> Child,BUFFER_COMMAND);
Wait(BUFFER_SYNC);
}
return(TRUE);
}
}
/* BufferFlush(struct Buffer *Buffer):
*
* Flush the contents of a given buffer to disk.
*/
STATIC BYTE __regargs
BufferFlush(struct Buffer *Buffer)
{
if(Buffer -> LastActionFailed)
return(FALSE);
else
{
if(Buffer -> BufPosition && Buffer -> Written)
{
Buffer -> Action = BUF_FLUSH;
Signal(Buffer -> Child,BUFFER_COMMAND);
Wait(BUFFER_SYNC);
}
return(TRUE);
}
}
/* IsValidBuffer(struct Buffer *Buffer):
*
* Scans the double buffered file list for
* a valid entry.
*/
STATIC BYTE __regargs
IsValidBuffer(struct Buffer *Buffer)
{
BYTE GotIt = FALSE;
struct Node *Node;
ObtainSemaphore(&DoubleBufferSemaphore);
Node = DoubleBufferList . lh_Head;
while(Node -> ln_Succ)
{
if(Buffer == (struct Buffer *)Node)
{
GotIt = TRUE;
break;
}
Node = Node -> ln_Succ;
}
ReleaseSemaphore(&DoubleBufferSemaphore);
return(GotIt);
}
/* BPrintf():
*
* Prints text into a buffered file.
*/
LONG __stdargs
BPrintf(struct Buffer *Buffer,UBYTE *Format,...)
{
UBYTE String[256];
va_list VarArgs;
va_start(VarArgs,Format);
VSPrintf(String,Format,VarArgs);
va_end(VarArgs);
return(BufferWrite(Buffer,String,strlen(String)));
}
/* BufferClose(struct Buffer *Buffer):
*
* Close a buffered filehandle.
*/
BYTE __regargs
BufferClose(struct Buffer *Buffer)
{
if(IsValidBuffer(Buffer))
{
BYTE Success;
Buffer -> Action = BUF_CLOSE;
Signal(Buffer -> Child,BUFFER_COMMAND);
Wait(BUFFER_SYNC);
Success = Buffer -> Result;
FreeVec(Buffer);
return(Success);
}
else
return(FALSE);
}
/* BufferOpen(UBYTE *Name,UBYTE *AccessMode):
*
* Open a file for buffered I/O.
*/
struct Buffer * __regargs
BufferOpen(UBYTE *Name,UBYTE *AccessMode)
{
struct Buffer *Buffer;
/* Allocate the buffer data. */
if(Buffer = (struct Buffer *)AllocVec(sizeof(struct Buffer) + BUFFER_SIZE * BUFFER_NUMBER,MEMF_ANY|MEMF_CLEAR))
{
struct Process *Process;
WORD i;
/* Set up the first buffer. */
Buffer -> DataBuffer[0] = (UBYTE *)(Buffer + 1);
/* Set up the individual buffers. */
for(i = 1 ; i < BUFFER_NUMBER ; i++)
Buffer -> DataBuffer[i] = &Buffer -> DataBuffer[i - 1][BUFFER_SIZE];
Buffer -> BufLength = BUFFER_SIZE;
Buffer -> WriteBufFull = Buffer -> BufLength;
/* Create the asynchronous file server. */
if(!(Process = CreateNewProcTags(
NP_Entry, FileBufferServer,
NP_Name, "term file process",
NP_Priority, SysBase -> ThisTask -> tc_Node . ln_Pri,
NP_StackSize, 8192,
NP_WindowPtr, -1,
TAG_DONE)))
{
FreeVec(Buffer);
return(NULL);
}
/* Set up the message header. */
Buffer -> Message . mn_Length = sizeof(struct Buffer);
Buffer -> ActionData[ARG_NAME] = (LONG)Name;
Buffer -> ActionData[ARG_MODE] = (LONG)AccessMode;
Buffer -> Child = Process;
Buffer -> Caller = (struct Process *)SysBase -> ThisTask;
/* Send it to the waiting server process. */
PutMsg(&Process -> pr_MsgPort,&Buffer -> Message);
/* Wait for ringback. */
Wait(BUFFER_SYNC);
/* Do we have a valid filehandle? */
if(!Buffer -> FileHandle)
{
FreeVec(Buffer);
return(NULL);
}
else
return(Buffer);
}
return(NULL);
}
/* BufferSeek(struct Buffer *Buffer,LONG Offset,LONG Origin):
*
* Move the read/write pointer to a specific position
* in a file (not really buffered).
*/
BYTE __regargs
BufferSeek(struct Buffer *Buffer,LONG Offset,LONG Origin)
{
Buffer -> Action = BUF_SEEK;
Buffer -> ActionData[ARG_OFFSET] = Offset;
Buffer -> ActionData[ARG_ORIGIN] = Origin;
Signal(Buffer -> Child,BUFFER_COMMAND);
Wait(BUFFER_SYNC);
if(Buffer -> Result == -1)
return(FALSE);
else
return(TRUE);
}
/* BufferRead():
*
* Read data from a file (buffered).
*/
LONG __regargs
BufferRead(struct Buffer *Buffer,UBYTE *Destination,LONG Size)
{
LONG BytesRead = 0,ToCopy,BufPosition,ReadBufFull;
UBYTE *Data;
/* If there is still data to be written in
* the buffer, write it.
*/
if(Buffer -> Written)
{
if(!BufferFlush(Buffer))
return(0);
}
/* Set up for read access. */
BufPosition = Buffer -> BufPosition;
ReadBufFull = Buffer -> ReadBufFull;
Data = &Buffer -> Data[BufPosition];
/* Continue until all data has been processed. */
while(Size)
{
/* Determine number of bytes to transfer. */
if(ToCopy = (Size > ReadBufFull) ? ReadBufFull : Size)
{
memcpy(Destination,Data,ToCopy);
Size -= ToCopy;
BufPosition += ToCopy;
ReadBufFull -= ToCopy;
Destination += ToCopy;
Data += ToCopy;
BytesRead += ToCopy;
}
else
{
/* Refill buffer with data. */
Buffer -> BufPosition = BufPosition;
Buffer -> ReadBufFull = ReadBufFull;
if(!BufferFill(Buffer))
return(BytesRead);
if(!Buffer -> ReadBufFull)
{
Buffer -> BufPosition = BufPosition;
return(BytesRead);
}
/* Pick up new data. */
BufPosition = Buffer -> BufPosition;
ReadBufFull = Buffer -> ReadBufFull;
Data = Buffer -> Data;
}
}
/* Install new data. */
Buffer -> BufPosition = BufPosition;
Buffer -> ReadBufFull = ReadBufFull;
return(BytesRead);
}
/* BufferWrite():
*
* Write data to a file (buffered).
*/
LONG __regargs
BufferWrite(struct Buffer *Buffer,UBYTE *Source,LONG Size)
{
LONG BytesWritten = 0,ToCopy,BufPosition,WriteBufFull;
UBYTE *Data;
/* If there is still read data in the buffer,
* reset the control information.
*/
if(Buffer -> Read)
{
Buffer -> WriteBufFull = Buffer -> BufLength;
Buffer -> BufPosition = 0;
Buffer -> Read = FALSE;
}
/* Set up for write access. */
Buffer -> Written = TRUE;
BufPosition = Buffer -> BufPosition;
WriteBufFull = Buffer -> WriteBufFull;
Data = &Buffer -> Data[BufPosition];
/* Continue until all data has been processed. */
while(Size)
{
/* Determine number of bytes to transfer. */
if(ToCopy = (Size > WriteBufFull ? WriteBufFull : Size))
{
memcpy(Data,Source,ToCopy);
Size -= ToCopy;
BufPosition += ToCopy;
WriteBufFull -= ToCopy;
Source += ToCopy;
Data += ToCopy;
BytesWritten += ToCopy;
}
else
{
/* Flush the contents of the
* write buffer.
*/
Buffer -> BufPosition = BufPosition;
Buffer -> WriteBufFull = WriteBufFull;
if(!BufferFlush(Buffer))
return(BytesWritten);
/* Pick up new data. */
BufPosition = Buffer -> BufPosition;
WriteBufFull = Buffer -> WriteBufFull;
Data = Buffer -> Data;
/* Important - or BufferFlush() won't
* write the final buffer contents when
* the buffered file handle is freed up.
*/
Buffer -> Written = TRUE;
}
}
/* Install new data. */
Buffer -> BufPosition = BufPosition;
Buffer -> WriteBufFull = WriteBufFull;
return(BytesWritten);
}